﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Titan.Serialization
{
    internal abstract class ObjectReaderWriter
    {
        protected abstract object InternalRead(BinaryReader reader);
        protected abstract void InternalWrite(BinaryWriter writer, object obj);

        public object Read(BinaryReader reader)
        {
            byte b = reader.ReadByte();
            if (b == 0)
                return DBNull.Value;
            else
                return InternalRead(reader);
        }

        public void Write(BinaryWriter writer, object obj)
        {
            if (obj == null || obj == DBNull.Value)
            {
                writer.Write((byte)0);
            }
            else
            {
                writer.Write((byte)1);
                InternalWrite(writer, obj);
            }
        }

        public static ObjectReaderWriter Create(Type type)
        {
            if (null == type)
                throw new ArgumentNullException("type");

            if (type == typeof(string))
                return new StringReaderWriter();
            else if (type == typeof(Int32))
                return new Int32ReaderWriter();
            else if (type == typeof(Decimal))
                return new DecimalReaderWriter();
            else if (type == typeof(DateTime))
                return new DateTimeReaderWriter();
            else if (type == typeof(Double))
                return new DoubleReaderWriter();
            else if (type == typeof(byte))
                return new ByteReaderWriter();
            else if (type == typeof(Int16))
                return new Int16ReaderWriter();
            else if (type == typeof(Char))
                return new CharReaderWriter();
            else if (type == typeof(Boolean))
                return new BooleanReaderWriter();
            else if (type == typeof(Int64))
                return new Int64ReaderWriter();
            else if (type == typeof(Single))
                return new SingleReaderWriter();
            else if (type == typeof(byte[]))
                return new ByteArrayReaderWriter();
            else if (type == typeof(char[]))
                return new CharArrayReaderWriter();
            else
                return new GenericReaderWriter();
        }

        private class Int16ReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                return reader.ReadInt16();
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                writer.Write((Int16)obj);
            }
        }

        private class Int32ReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                return reader.ReadInt32();
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                writer.Write((Int32)obj);
            }
        }

        private class Int64ReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                return reader.ReadInt64();
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                writer.Write((Int64)obj);
            }
        }


        private class ByteReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                return reader.ReadByte();
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                writer.Write((byte)obj);
            }
        }

        private class ByteArrayReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                int length = Bit7Encoding.Read7BitEncodedInt32(reader);
                return reader.ReadBytes(length);
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                byte[] array = (byte[])obj;
                Bit7Encoding.Write7BitEncodedInt32(writer, array.Length);
                writer.Write(array);
            }
        }

        private class StringReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                return reader.ReadString();
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                writer.Write((string)obj);
            }
        }

        private class DateTimeReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                long ticks = reader.ReadInt64();
                return new DateTime(ticks);
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                writer.Write(((DateTime)obj).Ticks);
            }
        }

        private class BooleanReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                return reader.ReadBoolean();
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                writer.Write((bool)obj);
            }
        }

        private class SingleReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                return reader.ReadSingle();
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                writer.Write((Single)obj);
            }
        }

        private class DoubleReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                return reader.ReadDouble();
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                writer.Write((Double)obj);
            }
        }

        private class DecimalReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                return reader.ReadDecimal();
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                writer.Write((Decimal)obj);
            }
        }

        private class CharReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                return reader.ReadChar();
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                writer.Write((Char)obj);
            }
        }

        private class CharArrayReaderWriter : ObjectReaderWriter
        {
            protected override object InternalRead(BinaryReader reader)
            {
                int length = Bit7Encoding.Read7BitEncodedInt32(reader);
                return reader.ReadChars(length);
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                char[] array = (char[])obj;
                Bit7Encoding.Write7BitEncodedInt32(writer, array.Length);
                writer.Write(array);
            }
        }

        private class GenericReaderWriter : ObjectReaderWriter
        {
            private BinaryFormatter _formatter = new BinaryFormatter();

            protected override object InternalRead(BinaryReader reader)
            {
                int length = Bit7Encoding.Read7BitEncodedInt32(reader);
                byte[] buff = reader.ReadBytes(length);
                MemoryStream stream = new MemoryStream(buff);
                return _formatter.Deserialize(stream);
            }

            protected override void InternalWrite(BinaryWriter writer, object obj)
            {
                MemoryStream stream = new MemoryStream();
                _formatter.Serialize(stream, obj);
                Bit7Encoding.Write7BitEncodedInt32(writer, (int)stream.Length);
                writer.Write(stream.GetBuffer(), 0, (int)stream.Length);
            }

        }
    }
}
